package engine;

/**
 * Vector.java
 * @author Kirk Roerig
 * 
 *	Describes a 2D vector, and includes a number of commonly used
 *	vector related mathematical functions.
 */
public class Vector {
	public static final Vector Zero = new Vector(0,0);
	private /*@ spec_public @*/ float x, y;

	
	/**
	 * Constructs a 2D vector object, used to describe
	 * positions, forces etc...
	 * @param x Desired x component
	 * @param y Desired y component.
	 * @assignable this.x, this.y;
	 */
	public Vector(float x, float y){
		this.x = x;
		this.y = y;
	}
	
	/**
	 * Add Vector 'v' to this Vector.
	 * @param v Vector to be added.
	 * @requires v != null;
	 * @assignable this.x, this.y;
	 * @ensures x += v.getX();
	 * @ensures y += v.getY();
	 */
	public void add(Vector v){
		if(v==null){System.out.println("Illegal Input, cannot have null vector");return;}
		x += v.getX();
		y += v.getY();
	}
	
	/**
	 * Subtract Vector 'v' from this Vector.
	 * @param v Vector to subtract.
	 * @requires v != null;
	 * @assignable this.x, this.y;
	 * @ensures x -= v.getX();
	 * @ensures y -= v.getY();
	 */
	public void sub(Vector v){
		if(v==null){System.out.println("Illegal Input, cannot have null vector");return;}
		x -= v.getX();
		y -= v.getY();
	}
	
	/**
	 * Multiply this vector by Vector 'v'.
	 * @param v Vector to multiply by.
	 * @requires v != null;
	 * @assignable this.x, this.y;
	 * @ensures x *= v.getX();
	 * @ensures y *= v.getY();
	 */
	public void mul(Vector v){
		if(v==null){System.out.println("Illegal Input, cannot have null vector");return;}
		x *= v.getX();
		y *= v.getY();
	}
	
	/**
	 * Divide this vector by Vector 'v'.
	 * @param v Vector to divide by.
	 * @requires v != null;
	 * @assignable this.x, this.y;
	 * @ensures x /= v.getX();
	 * @ensures y /= v.getY();
	 */
	public void div(Vector v){
		if(v==null){System.out.println("Illegal Input, cannot have null vector");return;}
		float tempX;
		float tempY;
		if(v.getX()==0){tempX = (float) .000001;}else tempX = v.getX();
		x /= tempX;
		if(v.getY()==0){tempY = (float) .000001;}else tempY = v.getY();
		y /= tempY;
	}
	
	/**
	 * Scale this Vector's components by scalar 's'.
	 * @param scalar Scale factor.
	 * @assignable this.x, this.y;
	 * @ensures x *= scalar;
	 * @ensures y *= scalar;
	 */
	public void scl(float scalar){
		x *= scalar;
		y *= scalar;
	}
	
	/**
	 * Takes the dot product between this Vector
	 * and the given Vector 'v'.
	 * @param v Other Vector.
	 * @return dot product result.
	 * @requires v != null;
	 * @ensures \result == (this.x * v.getX()) + (this.y * v.getY());
	 */
	public float dot(Vector v){
		if(v==null){System.out.println("Illegal Input, cannot have null vector");return 0;}
		return x * v.getX() + y * v.getY();
	}
	
	public float cross(Vector v){
		if(v==null){System.out.println("Illegal Input, cannot have null vector");return 0;}
		Matrix2X2 m = new Matrix2X2(x,		y,
									v.x,	v.y);
		return m.det();
	}
	
	/**
	 * Measures the distance between the point represented
	 * by this Vector, and the Vector 'v'.
	 * @param v Other Vector.
	 * @return Distance between this and 'v'.
	 * @requires v != null;
	 * @ensures \result >=0;
	 */
	public float dist(Vector v){
		if(v==null){System.out.println("Illegal Input, cannot have null vector");return 0;}
		float xsub = v.getX() - x;
		float ysub = v.getY() - y;
		return (float)Math.sqrt(xsub*xsub + ysub*ysub);
	}
	
	/**
	 * @ensures dist(Zero) == 1;
	 */
	public void normalize(){
		float dist = dist(Zero);
		scl(1f/dist);
	}
	/**
	 * magnitude gets the magnitude of the current vector
	 * @return the magnitude of the vector
	 */
	/*
	 * @returns float;
	 * @requires this!=null;
	 */
	public float magnitude()
	{
		return (float) Math.sqrt(((this.x*this.x)+(this.y*this.y)));
	}
	
	/*
	 * Static methods
	 */
	
	/**
	 * Returns the result of the addition of 'u' and 'v'.
	 * @param u Vector u.
	 * @param v Vector v.
	 * @return Addition of u and v.
	 * @requires u != null;
	 * @requires v != null;
	 * @assignable out;
	 * @ensures out != null;
	 * @ensures out.getX() == u.getX() + v.getX();
	 * @ensures out.getY() == u.getY() + v.getY();
	 * @ensures \result != null;
	 */
	public static Vector add(Vector u, Vector v){
		if(v==null||u==null){System.out.println("Illegal Input, cannot have null vector");return null;}
		Vector out = new Vector(u.getX() + v.getX(),
								u.getY() + v.getY());
		return out;
	}
	
	/**
	 * Returns the result of the subtraction of 'u' and 'v'.
	 * @param u Vector u.
	 * @param v Vector v.
	 * @return Subtraction of u and v.
	 * @requires u != null;
	 * @requires v != null;
	 * @assignable out;
	 * @ensures out != null;
	 * @ensures out.getX() == u.getX() - v.getX();
	 * @ensures out.getY() == u.getY() - v.getY();
	 * @ensures \result != null;
	 */
	public static Vector sub(Vector u, Vector v){
		if(v==null||u==null){System.out.println("Illegal Input, cannot have null vector");return null;}
		Vector out = new Vector(u.getX() - v.getX(),
								u.getY() - v.getY());
		return out;
	}
	
	/**
	 * Returns the result of the multiplication of 'u' and 'v'.
	 * @param u Vector u.
	 * @param v Vector v.
	 * @return Product of u and v.
	 * @requires u != null;
	 * @requires v != null;
	 * @assignable out;
	 * @ensures out != null;
	 * @ensures out.getX() == u.getX() * v.getX();
	 * @ensures out.getY() == u.getY() * v.getY();
	 * @ensures \result != null;
	 */
	public static Vector mul(Vector u, Vector v){
		if(v==null||u==null){System.out.println("Illegal Input, cannot have null vector");return null;}
		Vector out = new Vector(u.getX() * v.getX(),
								u.getY() * v.getY());
		return out;
	}
	
	
	/**
	 * Returns the result of the division of 'u' and 'v'.
	 * @param u Vector u.
	 * @param v Vector v.
	 * @return Division of u and v.
	 * @requires u != null;
	 * @requires v != null;
	 * @assignable out;
	 * @ensures out != null;
	 * @ensures out.getX() == u.getX() / v.getX();
	 * @ensures out.getY() == u.getY() / v.getY();
	 * @ensures \result != null;
	 */
	public static Vector div(Vector u, Vector v){
		if(v==null||u==null){System.out.println("Illegal Input, cannot have null vector");return null;}
		Vector out = new Vector(u.getX() / v.getX(),
								u.getY() / v.getY());
		return out;
	}
	
	/**
	 * Returns the result of the dot product of 'u' and 'v'.
	 * @param u Vector u.
	 * @param v Vector v.
	 * @return Dot product of u and v.
	 * @requires u != null;
	 * @requires v != null;
	 * @ensures \result == (u.getX() * v.getX()) + (u.getY() * v.getY());
	 */
	public static float dot(Vector u, Vector v){
		if(v==null||u==null){System.out.println("Illegal Input, cannot have null vector");return 0;}
		return u.x * v.x + u.y * v.y;
	}
	
	/**
	 * Returns the result of the distance between points 'u' and 'v'.
	 * @param u Vector u.
	 * @param v Vector v.
	 * @return Distance between u and v.
	 * @requires u != null;
	 * @requires v != null;
	 * @ensures \result >= 0;
	 */
	public static float dist(Vector u, Vector v){
		if(v==null||u==null){System.out.println("Illegal Input, cannot have null vector");return 0;}
		float xsub = v.x - u.x;
		float ysub = v.y - u.y;
		return (float)Math.sqrt(xsub*xsub + ysub*ysub);
	}
	
	/**
	 * Returns the result of the normalization of 'v'.
	 * @param v Vector v.
	 * @return Vector 'v' normalized.
	 * @requires v != null;
	 * @assignable out;
	 * @ensures out != null;
	 * @ensures \result != null;
	 */
	public static Vector normalize(Vector v){
		if(v==null){System.out.println("Illegal Input, cannot have null vector");return null;}
		Vector out = new Vector(v.x,v.y);
		float dist = out.dist(Zero);
		out.scl(1f/dist);
		return out;
	}
	
	/*
	 * Getters and Setters
	 */
	
	/**
	 * @return Component x.
	 * @ensures \result == this.x;
	 */
	public float getX() {
		return x;
	}

	/**
	 * @param x Component x.
	 * @assignable this.x;
	 * @ensures this.x == x;
	 */
	public void setX(float x) {
		this.x = x;
	}

	/**
	 * @return Component y.
	 * @ensures \result == this.y;
	 */
	public float getY() {
		return y;
	}

	/**
	 * @param y Component y.
	 * @assignable this.y;
	 * @ensures this.y == y;
	 */
	public void setY(float y) {
		this.y = y;
	}
	/**
	 * equals checks if this vector's x and y match the passed vector's x and y
	 * @param v1 the vector to check against
	 * @return true if equal, false if not
	 */
	/*
	 * @requires this!=null;
	 * @requires v1!=null;
	 * @assignable \nothing;
	 */
	public boolean equals(Vector v1){
		if(v1==null){System.out.println("Illegal Input, cannot have null vector");return false;}
		return v1.getX() == x && v1.getY() == y;
	}
	/**
	 * Returns a String representation of the vector using its X and Y values
	 */
	/*
	 * @returns new String
	 * @assignable \nothing
	 */
	public String toString()
	{
		return " "+x+","+y+"\n"; 
	}
}
